#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <string>
#include <vector>
#include <functional>
#include <unistd.h>
#include <sys/wait.h>

////////////////////////////////////////子进程任务////////////////////////////////////////
void SyncDick()
{
    std::cout << getpid() << "：刷行数据到磁盘" << std::endl;
    sleep(1);
}

void Download()
{
    std::cout << getpid() << "：下载数据到系统中" << std::endl;
    sleep(1);
}

void PrintLog()
{
    std::cout << getpid() << "：打印日志到本地" << std::endl;
    sleep(1);
}

void UpdateStatus()
{
    std::cout << getpid() << "：更新一次用户的状态" << std::endl;
    sleep(1);
}

typedef void (*task_t)(); // 函数指针

task_t tasks[4] = {SyncDick, Download, PrintLog, UpdateStatus}; // 任务表 ---面向对象化

////////////////////////////////////////进程池相关////////////////////////////////////////
enum
{
    OK = 0,
    PIPE_ERROR,
    FORK_ERROR
};

// 子进程的入口函数
void DoTask(int fd)
{
    while (true)
    {
        int task_code = 0;
        ssize_t n = read(fd, &task_code, sizeof(task_code));
        if (n == sizeof(task_code))
        {
            if (task_code >= 0 && task_code < 4)
            {
                tasks[task_code]();
            }
        }
        else if (n == 0)
        {
            std::cout << getpid() << "：task quie ..." << std::endl;
            break;
        }
        else
        {
            perror("read");
            break;
        }
    }
}

// 创建子进程数
const int gprocessnum = 5;
// 包装起包装void(int)类型的子进程入口函数
using cb_t = std::function<void(int)>;

class ProcessPool
{
private:
    class Channel
    {
    public:
        Channel(int wfd, pid_t pid)
            : _wfd(wfd), _sub_pid(pid)
        {
            _sub_name = "sub-channel-" + std::to_string(_sub_pid);
        }

        ~Channel() {}

        void Write(int index)
        {
            ssize_t n = write(_wfd, &index, sizeof(index));
            (void)n;
        }

        std::string Name()
        {
            return _sub_name;
        }

        void ClosePipe()
        {
            std::cout << "关闭wfd：" << _wfd << std::endl;
            close(_wfd);
        }

        void Wait()
        {
            pid_t rid = waitpid(_sub_pid, nullptr, 0);
            (void)rid;
            std::cout << "回收子进程：" << _sub_pid << std::endl;
        }

        void PrintInfo()
        {
            printf("wfd: %d, who: %d, channel name: %s\n", _wfd, _sub_pid, _sub_name.c_str());
        }

    private:
        int _wfd;              // 1. wfd
        pid_t _sub_pid;        // 2. 子进程是谁
        std::string _sub_name; // 3. 子channel的名字
    };

public:
    ProcessPool()
    {
        srand((unsigned int)time(NULL) ^ getpid());
    }

    ~ProcessPool() {}

    void Init(cb_t cb)
    {
        CreateProcessChannel(cb);
    }

    void Run()
    {
        // 需要分配的任务的总个数
        int cnt = 10;

        while (cnt--)
        // while(true)
        {
            std::cout << "---------------------------------------------------" << std::endl;

            // 1.选择一个任务
            int itask = SelectTask();
            std::cout << "itask: " << itask << std::endl;

            // 2.选择一个channel(管道+子进程)，本质：选择一个下标数字
            int index = SelectChannel();
            std::cout << "index: " << index << std::endl;

            // 3.发送一个任务给指定的channel(管道+子进程)
            printf("发送 %d to %s\n", itask, channels[index].Name().c_str());
            SendTask2Salver(itask, index);
            sleep(1); // 一秒一个任务
        }
    }

    void Quit()
    {
        // version3:
        for (auto &channel : channels)
        {
            channel.ClosePipe();
            channel.Wait();
        }

        // version2: 逆向回收
        //  int end=channels.size()-1;
        //  while(end>=0)
        //  {
        //      channels[end].ClosePipe();
        //      channels[end].Wait();
        //      end--;
        //  }

        // bug演示
        // for (auto &channel : channels) // 为什么不能这样写？应该怎么写？bug?
        // {
        //     channel.ClosePipe();
        //     channel.Wait();
        // }

        // version1
        // // 1. 让所有子进程退出
        // for (auto &channel : channels)
        // {
        //     channel.ClosePipe();
        // }

        // // 2. 回收子进程
        // for (auto &channel : channels)
        // {
        //     channel.Wait();
        // }
    }

    void Debug()
    {
        for (auto &c : channels)
        {
            c.PrintInfo();
        }
    }

private:
    void CreateProcessChannel(cb_t cb)
    {
        // 1.创建多个管道和多个进程
        for (int i = 0; i < gprocessnum; i++)
        {
            int pipefd[2] = {0};
            int n = pipe(pipefd);
            if (n < 0)
            {
                std::cerr << "pipe creat error" << std::endl;
                exit(PIPE_ERROR);
            }
            pid_t id = fork();
            if (id < 0)
            {
                std::cerr << "fork error" << std::endl;
                exit(FORK_ERROR);
            }
            else if (id == 0)
            {
                // 子进程

                // 子进程关闭历史wfd,影响的是自己的fd表
                if (!channels.empty())
                {
                    for (auto &c : channels)
                    {
                        c.ClosePipe();
                    }
                }

                // 关闭子进程的写入文件操作符
                close(pipefd[1]);
                cb(pipefd[0]);
                exit(OK);
            }
            else
            {
                // 父进程

                // 关闭父进程的写入文件操作符
                close(pipefd[0]);
                // 将创建好的管道放入vector容器
                channels.emplace_back(pipefd[1], id);
                std::cout << "创建子进程：" << id << "成功..." << std::endl;
                sleep(1);
            }
        }
    }

    // 选择方法：随机法
    int SelectTask()
    {
        int itask = rand() % 4;
        return itask;
    }

    // 选择方法：轮转法
    int SelectChannel()
    {
        static int index = 0;
        int select = index;
        index++;
        index %= channels.size();

        return select;
    }

    void SendTask2Salver(int itask, int index)
    {
        if (itask >= 4 || itask < 0)
            return;
        if (index < 0 || index >= channels.size())
            return;

        channels[index].Write(itask);
    }

private:
    // 组织所有channel的容器
    std::vector<Channel> channels;
};

int main()
{
    // 1.初始化进程池
    ProcessPool pp;
    pp.Init(DoTask);
    pp.Debug();

    // 2.父进程控制子进程，channels
    pp.Run();

    // 3.释放和回收所有资源(释放管道，回收子进程)
    pp.Quit();

    return 0;
}